using ICSharpCode.TextEditor.Document;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace ICSharpCode.TextEditor
{
    public class Caret : IDisposable
    {
        private abstract class CaretImplementation : IDisposable
        {
            public bool RequireRedrawOnPositionChange;
            public abstract bool Create(int width, int height);
            public abstract void Hide();
            public abstract void Show();
            public abstract bool SetPosition(int x, int y);
            public abstract void PaintCaret(Graphics g);
            public abstract void Destroy();
            public virtual void Dispose()
            {
                this.Destroy();
            }
        }
        private class ManagedCaret : Caret.CaretImplementation
        {
            private Timer timer = new Timer
            {
                Interval = 300
            };
            private bool visible;
            private bool blink = true;
            private int x;
            private int y;
            private int width;
            private int height;
            private TextArea textArea;
            private Caret parentCaret;
            public ManagedCaret(Caret caret)
            {
                this.RequireRedrawOnPositionChange = true;
                this.textArea = caret.textArea;
                this.parentCaret = caret;
                this.timer.Tick += new EventHandler(this.CaretTimerTick);
            }
            private void CaretTimerTick(object sender, EventArgs e)
            {
                this.blink = !this.blink;
                if(this.visible)
                {
                    this.textArea.UpdateLine(this.parentCaret.Line);
                }
            }
            public override bool Create(int width, int height)
            {
                this.visible = true;
                this.width = width - 2;
                this.height = height;
                this.timer.Enabled = true;
                return true;
            }
            public override void Hide()
            {
                this.visible = false;
            }
            public override void Show()
            {
                this.visible = true;
            }
            public override bool SetPosition(int x, int y)
            {
                this.x = x - 1;
                this.y = y;
                return true;
            }
            public override void PaintCaret(Graphics g)
            {
                if(this.visible && this.blink)
                {
                    g.DrawRectangle(Pens.Gray, this.x, this.y, this.width, this.height);
                }
            }
            public override void Destroy()
            {
                this.visible = false;
                this.timer.Enabled = false;
            }
            public override void Dispose()
            {
                base.Dispose();
                this.timer.Dispose();
            }
        }
        private class Win32Caret : Caret.CaretImplementation
        {
            private TextArea textArea;
            [DllImport("User32.dll")]
            private static extern bool CreateCaret(IntPtr hWnd, int hBitmap, int nWidth, int nHeight);
            [DllImport("User32.dll")]
            private static extern bool SetCaretPos(int x, int y);
            [DllImport("User32.dll")]
            private static extern bool DestroyCaret();
            [DllImport("User32.dll")]
            private static extern bool ShowCaret(IntPtr hWnd);
            [DllImport("User32.dll")]
            private static extern bool HideCaret(IntPtr hWnd);
            public Win32Caret(Caret caret)
            {
                this.textArea = caret.textArea;
            }
            public override bool Create(int width, int height)
            {
                return Caret.Win32Caret.CreateCaret(this.textArea.Handle, 0, width, height);
            }
            public override void Hide()
            {
                Caret.Win32Caret.HideCaret(this.textArea.Handle);
            }
            public override void Show()
            {
                Caret.Win32Caret.ShowCaret(this.textArea.Handle);
            }
            public override bool SetPosition(int x, int y)
            {
                return Caret.Win32Caret.SetCaretPos(x, y);
            }
            public override void PaintCaret(Graphics g)
            {
            }
            public override void Destroy()
            {
                Caret.Win32Caret.DestroyCaret();
            }
        }
        private int line;
        private int column;
        private int desiredXPos;
        private CaretMode caretMode;
        private static bool caretCreated;
        private bool hidden = true;
        private TextArea textArea;
        private Point currentPos = new Point(-1, -1);
        private Ime ime;
        private Caret.CaretImplementation caretImplementation;
        private int oldLine = -1;
        private bool outstandingUpdate;
        private bool firePositionChangedAfterUpdateEnd;
        public event EventHandler PositionChanged;
        public event EventHandler CaretModeChanged;
        public int DesiredColumn
        {
            get
            {
                return this.desiredXPos;
            }
            set
            {
                this.desiredXPos = value;
            }
        }
        public CaretMode CaretMode
        {
            get
            {
                return this.caretMode;
            }
            set
            {
                this.caretMode = value;
                this.OnCaretModeChanged(EventArgs.Empty);
            }
        }
        public int Line
        {
            get
            {
                return this.line;
            }
            set
            {
                this.line = value;
                this.ValidateCaretPos();
                this.UpdateCaretPosition();
                this.OnPositionChanged(EventArgs.Empty);
            }
        }

        public LineSegment LineSegment
        {
            get
            {
                return textArea.Document.GetLineSegment(this.line);
            }
        }

        public int Column
        {
            get
            {
                return this.column;
            }
            set
            {
                this.column = value;
                this.ValidateCaretPos();
                this.UpdateCaretPosition();
                this.OnPositionChanged(EventArgs.Empty);
            }
        }
        public TextLocation Position
        {
            get
            {
                return new TextLocation(this.column, this.line);
            }
            set
            {
                this.line = value.Y;
                this.column = value.X;
                this.ValidateCaretPos();
                this.UpdateCaretPosition();
                this.OnPositionChanged(EventArgs.Empty);
            }
        }
        public int Offset
        {
            get
            {
                return this.textArea.Document.PositionToOffset(this.Position);
            }
        }
        public Point ScreenPosition
        {
            get
            {
                int drawingXPos = this.textArea.TextView.GetDrawingXPos(this.line, this.column);
                return new Point(this.textArea.TextView.DrawingPosition.X + drawingXPos, this.textArea.TextView.DrawingPosition.Y + this.textArea.Document.GetVisibleLine(this.line) * this.textArea.TextView.FontHeight - this.textArea.TextView.TextArea.VirtualTop.Y);
            }
        }
        public Caret(TextArea textArea)
        {
            this.textArea = textArea;
            textArea.GotFocus += new EventHandler(this.GotFocus);
            textArea.LostFocus += new EventHandler(this.LostFocus);
            if(Environment.OSVersion.Platform == PlatformID.Unix)
            {
                this.caretImplementation = new Caret.ManagedCaret(this);
                return;
            }
            this.caretImplementation = new Caret.Win32Caret(this);
        }
        public void Dispose()
        {
            this.textArea.GotFocus -= new EventHandler(this.GotFocus);
            this.textArea.LostFocus -= new EventHandler(this.LostFocus);
            this.textArea = null;
            this.caretImplementation.Dispose();
        }
        public TextLocation ValidatePosition(TextLocation pos)
        {
            int lineNumber = Math.Max(0, Math.Min(this.textArea.Document.TotalNumberOfLines - 1, pos.Y));
            int num = Math.Max(0, pos.X);
            if(num == 2147483647 || !this.textArea.TextEditorProperties.AllowCaretBeyondEOL)
            {
                LineSegment lineSegment = this.textArea.Document.GetLineSegment(lineNumber);
                num = Math.Min(num, lineSegment.Length);
            }
            return new TextLocation(num, lineNumber);
        }
        public void ValidateCaretPos()
        {
            this.line = Math.Max(0, Math.Min(this.textArea.Document.TotalNumberOfLines - 1, this.line));
            this.column = Math.Max(0, this.column);
            if(this.column == 2147483647 || !this.textArea.TextEditorProperties.AllowCaretBeyondEOL)
            {
                LineSegment lineSegment = this.textArea.Document.GetLineSegment(this.line);
                this.column = Math.Min(this.column, lineSegment.Length);
            }
        }
        private void CreateCaret()
        {
            while(!Caret.caretCreated)
            {
                switch(this.caretMode)
                {
                    case CaretMode.InsertMode:
                        {
                            Caret.caretCreated = this.caretImplementation.Create(2, this.textArea.TextView.FontHeight);
                            break;
                        }
                    case CaretMode.OverwriteMode:
                        {
                            Caret.caretCreated = this.caretImplementation.Create(this.textArea.TextView.SpaceWidth, this.textArea.TextView.FontHeight);
                            break;
                        }
                }
            }
            if(this.currentPos.X < 0)
            {
                this.ValidateCaretPos();
                this.currentPos = this.ScreenPosition;
            }
            this.caretImplementation.SetPosition(this.currentPos.X, this.currentPos.Y);
            this.caretImplementation.Show();
        }
        public void RecreateCaret()
        {
            this.DisposeCaret();
            if(!this.hidden)
            {
                this.CreateCaret();
            }
        }
        private void DisposeCaret()
        {
            if(Caret.caretCreated)
            {
                Caret.caretCreated = false;
                this.caretImplementation.Hide();
                this.caretImplementation.Destroy();
            }
        }
        private void GotFocus(object sender, EventArgs e)
        {
            this.hidden = false;
            if(!this.textArea.MotherTextEditorControl.IsInUpdate)
            {
                this.CreateCaret();
                this.UpdateCaretPosition();
            }
        }
        private void LostFocus(object sender, EventArgs e)
        {
            this.hidden = true;
            this.DisposeCaret();
        }
        internal void OnEndUpdate()
        {
            if(this.outstandingUpdate)
            {
                this.UpdateCaretPosition();
            }
        }
        private void PaintCaretLine(Graphics g)
        {
            if(!this.textArea.Document.TextEditorProperties.CaretLine)
            {
                return;
            }
            HighlightColor colorFor = this.textArea.Document.HighlightingStrategy.GetColorFor("CaretLine");
            g.DrawLine(BrushRegistry.GetDotPen(colorFor.Color), this.currentPos.X, 0, this.currentPos.X, this.textArea.DisplayRectangle.Height);
        }
        public void UpdateCaretPosition()
        {
            if(this.textArea.TextEditorProperties.CaretLine)
            {
                this.textArea.Invalidate();
            }
            else
            {
                if(this.caretImplementation.RequireRedrawOnPositionChange)
                {
                    this.textArea.UpdateLine(this.oldLine);
                    if(this.line != this.oldLine)
                    {
                        this.textArea.UpdateLine(this.line);
                    }
                }
                else
                {
                    if(this.textArea.MotherTextAreaControl.TextEditorProperties.LineViewerStyle == LineViewerStyle.FullRow && this.oldLine != this.line)
                    {
                        this.textArea.UpdateLine(this.oldLine);
                        this.textArea.UpdateLine(this.line);
                    }
                }
            }
            this.oldLine = this.line;
            if(this.hidden || this.textArea.MotherTextEditorControl.IsInUpdate)
            {
                this.outstandingUpdate = true;
                return;
            }
            this.outstandingUpdate = false;
            this.ValidateCaretPos();
            int logicalLine = this.line;
            int drawingXPos = this.textArea.TextView.GetDrawingXPos(logicalLine, this.column);
            Point screenPosition = this.ScreenPosition;
            if(drawingXPos >= 0)
            {
                this.CreateCaret();
                if(!this.caretImplementation.SetPosition(screenPosition.X, screenPosition.Y))
                {
                    this.caretImplementation.Destroy();
                    Caret.caretCreated = false;
                    this.UpdateCaretPosition();
                }
            }
            else
            {
                this.caretImplementation.Destroy();
            }
            if(this.ime == null)
            {
                this.ime = new Ime(this.textArea.Handle, this.textArea.Document.TextEditorProperties.Font);
            }
            else
            {
                this.ime.HWnd = this.textArea.Handle;
                this.ime.Font = this.textArea.Document.TextEditorProperties.Font;
            }
            this.ime.SetIMEWindowLocation(screenPosition.X, screenPosition.Y);
            this.currentPos = screenPosition;
        }
        [Conditional("DEBUG")]
        private static void Log(string text)
        {
        }
        internal void PaintCaret(Graphics g)
        {
            this.caretImplementation.PaintCaret(g);
            this.PaintCaretLine(g);
        }
        private void FirePositionChangedAfterUpdateEnd(object sender, EventArgs e)
        {
            this.OnPositionChanged(EventArgs.Empty);
        }
        protected virtual void OnPositionChanged(EventArgs e)
        {
            if(this.textArea.MotherTextEditorControl.IsInUpdate)
            {
                if(!this.firePositionChangedAfterUpdateEnd)
                {
                    this.firePositionChangedAfterUpdateEnd = true;
                    this.textArea.Document.UpdateCommited += new EventHandler(this.FirePositionChangedAfterUpdateEnd);
                }
                return;
            }
            if(this.firePositionChangedAfterUpdateEnd)
            {
                this.textArea.Document.UpdateCommited -= new EventHandler(this.FirePositionChangedAfterUpdateEnd);
                this.firePositionChangedAfterUpdateEnd = false;
            }
            List<FoldMarker> foldingsFromPosition = this.textArea.Document.FoldingManager.GetFoldingsFromPosition(this.line, this.column);
            bool flag = false;
            foreach(FoldMarker current in foldingsFromPosition)
            {
                flag |= current.IsFolded;
                current.IsFolded = false;
            }
            if(flag)
            {
                this.textArea.Document.FoldingManager.NotifyFoldingsChanged(EventArgs.Empty);
            }
            if(this.PositionChanged != null)
            {
                this.PositionChanged(this, e);
            }
            this.textArea.ScrollToCaret();
        }
        protected virtual void OnCaretModeChanged(EventArgs e)
        {
            if(this.CaretModeChanged != null)
            {
                this.CaretModeChanged(this, e);
            }
            this.caretImplementation.Hide();
            this.caretImplementation.Destroy();
            Caret.caretCreated = false;
            this.CreateCaret();
            this.caretImplementation.Show();
        }
    }
}
